﻿using System;
using System.Collections;

namespace Testy
{
    /// <summary>
    /// Delegat, ktory reprezentuje funkcje, ktora bedzie wykonana przy
    /// przechodzeniu po drzewie.
    /// </summary>
    /// <param name="nodeData">Wartosc danego elementu drzewa.</param>
    public delegate void TreeVisitor( int nodeData );

    /// <summary>
    /// Interfejs udostepniajacy metode uruchamiajaca przechodzenie po drzewie.
    /// </summary>
    public interface ITraversable
    {
        /// <summary>
        /// Przejdz po drzewie i wykonaj metode wizytora.
        /// </summary>
        /// <param name="visitor">Delegat, ktory ma zostac wykonany dla kazdego
        /// elementu drzewa.</param>
        void Traverse( TreeVisitor visitor );
    }

    /// <summary>
    /// Drzewo.
    /// </summary>
    public class Tree : ITraversable
    {
        #region Stale
        /// <summary>
        /// Indeks pierwszego elemntu drzewa.
        /// </summary>
        private const int FIRST_ELEMENT = 0;
        #endregion

        #region Pola prywatne
        /// <summary>
        /// Wartosc przechowywana w korzeniu drzewa.
        /// </summary>
        private int data;
        /// <summary>
        /// Lista wszystkich poddrzew.
        /// </summary>
        protected IList children = new ArrayList();
        #endregion

        #region Wlasciwosci
        /// <summary>
        /// Pobiera i ustawia wartosc w korzeniu drzewa.
        /// </summary>
        public int Data
        {
            get
            {
                return data;
            }
            set
            {
                data = value;
            }
        }
        /// <summary>
        /// Pobiera liczbe poddrzew.
        /// </summary>
        public int ChildCount 
        {
            get
            {
                return children.Count;
            }
        }
        /// <summary>
        /// Pobiera lub ustawia poddrzewo o podanym indeksie.
        /// </summary>
        /// <param name="i">Indeks poddrzewa</param>
        /// <returns>Poddrzewo o podanym indeksie.</returns>
        public Tree this[int i]
        {
            get
            {
                return (Tree)children[i];
            }
            set
            {
                children[i] = value;
            }
        }
        #endregion

        #region Konstruktor
        /// <summary>
        /// Inicjalizuje drzewo i nadaje wartosc jego korzeniowi.
        /// </summary>
        /// <param name="data">Wartosc.</param>
        public Tree( int data )
        {
            this.data = data;
        }
        #endregion

        #region Metody publiczne
        /// <summary>
        /// Pobiera pierwszy element drzewa,
        /// </summary>
        /// <param name="firstElement">Zwraca wartosc korzenia pierwszego poddrzewa.</param>
        /// <returns>True jezeli poddrzewo istnieje, false w przeciwnym wypadku.</returns>
        public bool GetFirstChild(out int firstElement)
        {
            if ( children.Count > FIRST_ELEMENT )
            {
                firstElement = (Tree)children[FIRST_ELEMENT];
                return true;
            }
            else
            {
                firstElement = int.MinValue;
                return false;
            }
        }
        /// <summary>
        /// Dodaj poddrzewo i ustaw wartosc jego korzenia.
        /// </summary>
        /// <param name="data">Wartosc.</param>
        public virtual void AddChild( int data )
        {
            children.Add( new Tree( data ) );
        }
        /// <summary>
        /// Dodaj wieksza liczbe poddrzew i ustaw wartosci ich korzeni.
        /// </summary>
        /// <param name="data">Wartosci korzeni kolejnych poddrzew.</param>
        public virtual void AddChild( params int[] data )
        {
            foreach ( int item in data )
            {
                AddChild( item );
            }
        }
        /// <summary>
        /// Przejdz po drzewie i wykonaj metode wizytora dla
        /// kazdego elementu.
        /// </summary>
        /// <param name="visitor">Delegat, ktory bedzie wywolany dla kazdego elementu.</param>
        public void Traverse( TreeVisitor visitor )
        {
            visitor( data );
            foreach ( Tree kid in children )
                Traverse( kid, visitor );
        }
        /// <summary>
        /// Wyswietla na ekranie wartosc korzenia drzewa.
        /// </summary>
        /// <returns>Wartosc.</returns>
        public override string ToString()
        {
            return string.Format( "Drzewo o wartosci {0}", data );
        }
        #endregion

        #region Metody prywatne
        /// <summary>
        /// Przejdz po drzewie i wykonaj metode wizytora dla
        /// kazdego elementu.
        /// </summary>
        /// <param name="visitor">Delegat, ktory bedzie wywolany dla kazdego elementu.</param>
        /// <param name="node">Drzewo od krotergo zaczyac przechdzenie.</param>
        private void Traverse( Tree node, TreeVisitor visitor )
        {
            if ( node == null )
                return;

            visitor( node.data );
            foreach ( Tree kid in node.children )
                Traverse( kid, visitor );
        }
        #endregion

        #region Operatory
        /// <summary>
        /// Laczy ze soba dwa drzewa sumujac wartosc w korzeniu i laczac zbiory poddrzew.
        /// </summary>
        /// <param name="tree1">Pierwsze drzewo.</param>
        /// <param name="tree2">Drugie drzewo.</param>
        /// <returns>Nowe drzewo, ktore jest suma dwoch podanych drzew.</returns>
        public static Tree operator +( Tree tree1, Tree tree2 )
        {
            if ( tree1 == null )
                throw new ArgumentNullException( "tree1" );
            if ( tree1 == null )
                throw new ArgumentNullException( "tree1" );

            Tree result = new Tree( tree1.Data + tree2.Data );
            foreach ( Tree tree in tree1.children )
            {
                result.AddChild( tree.Data );
            }
            foreach ( Tree tree in tree2.children )
            {
                result.AddChild( tree.Data );
            }
            return result;
        }
        /// <summary>
        /// Konwertuje drzewo na wartosc calkowita, ktora zostala
        /// umieszczna  w korzeniu.
        /// </summary>
        /// <param name="tree">Drzewo do konwersji na inta.</param>
        /// <returns>Wartosc drzewa.</returns>
        public static implicit operator int( Tree tree )
        {
            return tree.Data;
        }
        #endregion

        #region Destruktor
        /// <summary>
        /// Destruktor - wyeswietla na ekranie ifnormacje na temad usuwanego obiektu.
        /// </summary>
        ~Tree()
        {
            Console.WriteLine("Destruktor " + ToString());
        }
        #endregion
    }

    /// <summary>
    /// Delegat reprezentujacy metode, ktora bedzie wykonana w momencie, gdy
    /// dodanie zostanie nowe poddrzewo.
    /// </summary>
    /// <param name="sender">Zrodlo zdarzenia.</param>
    /// <param name="child">Nowe poddrzewo.</param>
    /// <param name="position">Pozyca poddrzewa w drzewie.</param>
    public delegate void AddedChildEventHandler( object sender, Tree child, int position );

    /// <summary>
    /// Rozszerzone drzewo.
    /// </summary>
    public class SuperTree : Tree
    {
        #region Konstruktor
        /// <summary>
        /// Konstruktor, inicjalizuje klase.
        /// </summary>
        /// <param name="data"></param>
        public SuperTree( int data )
            : base( data )
        {
        }
        #endregion

        #region Metody publiczne
        public override void AddChild( int data )
        {
            base.AddChild( data );
            if ( AddedChild != null )
            {
                AddedChild( this, (Tree)children[children.Count - 1], children.Count - 1 );
            }
        }
        #endregion

        #region Zdarzenia
        /// <summary>
        /// Wystepuje w momencie, gdy zostaje dodany nowy element drzewa.
        /// </summary>
        public event AddedChildEventHandler AddedChild;
        #endregion
    }
}
